home *** CD-ROM | disk | FTP | other *** search
/ ShareWare OnLine 2 / ShareWare OnLine Volume 2 (CMS Software)(1993).iso / util2 / zdir18.zip / ZDIR18.ASM < prev    next >
Assembly Source File  |  1993-01-27  |  47KB  |  1,898 lines

  1. ;ZDIR .ZIP directory utility
  2. ;From a disassembly and hack of ADIR.EXE
  3.  
  4. ;v1.8  Update for PKZIP v2.04x
  5. ; - Added new "deflate" method.
  6. ; - Moved versions out to HISTORY.TXT
  7. ; - Removed earlier "v1.x" comments
  8.  
  9. FALSE    equ    0
  10. TRUE    equ    NOT FALSE
  11. PLAIN    equ    0
  12. VERBO    equ    0FFh
  13. MONOSYL equ    1
  14. STRINGWILD Equ    '*'            ; wildcard for strings
  15. CHARWILD Equ    '?'            ; wildcard for single chars
  16. CR    equ    0DH
  17. LF    equ    0AH
  18. STDOUT    equ    1
  19. STDERR    equ    2
  20. ENDOFS    equ    4096        ;enough for largest possible
  21.                 ;ZIP end directory structure.
  22.                 ;(to include XMODEM padding)
  23.                 ;(and hormongous comments!)
  24.                 ;(was 256 in v1.3)
  25.  
  26. Print    macro    name            ; display a field
  27.     mov    dx,offset name
  28.     call    PrintS
  29.     endm
  30.  
  31. ;PKZIP central directory structure:
  32.  
  33. zdirEntry    STRUC
  34. zsig1    db    50H,4BH,01H,02H ;central file header signature    4 bytes
  35.                 ;(0x02014b50)
  36. zVerMade    dw    ?    ;version made by        2 bytes
  37. zVerExt        dw    ?    ;version needed to extract    2 bytes
  38. zBitFlag    dw    ?    ;general purpose bit flag    2 bytes
  39. zCmpMeth    dw    ?    ;compression method        2 bytes
  40. zModTime    dw    ?    ;last mod file time        2 bytes
  41. zModDate    dw    ?    ;last mod file date        2 bytes
  42. zCrc32        dw    ?,?    ;crc-32                4 bytes
  43. zCmpSiz        dw    ?,?    ;compressed size        4 bytes
  44. zUncmpSiz    dw    ?,?    ;uncompressed size        4 bytes
  45. zNameLen    dw    ?    ;filename length        2 bytes
  46. zExtraLen    dw    ?    ;extra field length        2 bytes
  47. zFilCmtLen    dw    ?    ;file comment length        2 bytes
  48. zDskNrPtr    dw    ?    ;disk number start        2 bytes
  49. zIntAttr    dw    ?    ;internal file attributes    2 bytes
  50. zExtAttr    dw    ?,?    ;external file attributes    4 bytes
  51. zHdrOfs        dw    ?,?    ;relative offset of local header 4 bytes
  52. zFilename    db    ?    ;filename (variable size)
  53.                 ;extra field (variable size)
  54.                 ;file comment (variable size)
  55. zdirEntry    ENDS
  56.  
  57. ;    End of central dir record:
  58.  
  59. zdirEnd STRUC
  60. zEndSig    db    50H,4BH,05H,06H ;end of central dir signature    4 bytes
  61.                 ;(0x06054b50)
  62. zDskNr    dw    ?        ;number of this disk        2 bytes
  63. zDirDsk    dw    ?        ;number of the disk with the
  64.                 ;start of the central directory 2 bytes
  65. zDskNrEntry dw    ?        ;total number of entries in
  66.                 ;the central dir on this disk    2 bytes
  67. zDirNrEntry dw    ?        ;total number of entries in
  68.                 ;the central dir        2 bytes
  69. zDirSiz    dw    ?,?        ;size of the central directory    4 bytes
  70. zDirOfs    dw    ?,?        ;offset of start of central
  71.                 ;directory with respect to
  72.                 ;the starting disk number    4 bytes
  73. zCmtLen    dw    ?        ;zipfile comment length        2 bytes
  74. zCmt    db    ?        ;zipfile comment (variable size)
  75. zdirEnd ENDS
  76.  
  77.  
  78. CSEG    segment para public
  79.     assume    CS:CSEG,DS:CSEG
  80.  
  81.     org    5CH        ;FCB #1
  82.     db    ?        ;drive val
  83. fcb1    db    10H dup(?)    ;5DH, FCB #1 first char
  84. fcb2    db    10H dup(?)    ;6DH, FCB #2 first char
  85.     org    80H
  86. nchar    db    ?
  87. params    db    ?
  88.     org    9EH        ; We'll use the default PSP DTA,
  89. PSP_DTA_Name db ?        ; and the file name found will be HERE.
  90.  
  91. ;program entry point
  92.     org    100H
  93.  
  94. Zdir    proc    near
  95.     jmp    Start        ;skip over runtime data
  96.  
  97.  
  98. usage    db    CR,LF,9,9, 'ZDIR version 1.8, 920124',CR,LF
  99.     db    9,9,'David Kirschbaum, Toad Hall/mod GWS',CR,LF
  100.     db    9,9, 'USAGE: ',9,'ZDIR zipname[.zip] [afn] [-v|-m]',CR,LF
  101.     db    9,9,9,'zipname may be ambiguous (wildcarded)',CR,LF
  102.     db    9,9,9,'afn = ambiguous member file name (like a*b)', CR,LF
  103.     db    9,9,9,'-v  = verbose display',CR,LF
  104.     db    9,9,9,'-m  = monosyllabic display',CR,LF
  105. USAGELEN equ    $ - usage
  106.  
  107. ziptyp    db    '.ZIP'
  108. ZIPTYPLEN    equ    $ - ziptyp
  109.  
  110. msg1    db    CR,LF, 9, 9, 9, ' ZIP file: '
  111. MSG1LEN equ    $ - msg1
  112. msg2    db    'ZIP file not found',CR,LF
  113. MSG2LEN equ    $ - msg2
  114. msg3    db    'Central directory not found',CR,LF
  115. MSG3LEN equ    $ - msg3
  116. msg4    db    'ZIP is out of alignment or it''s not a ZIP'
  117. crlf    db    CR,LF
  118. MSG4LEN equ    $ - msg4
  119. CRLFLEN equ    $ - crlf
  120.  
  121. ;Keith Petersen suggested this oughtta go .. Sigh ...
  122. ;rivvvt    db    'Rivvvvt',CR,LF
  123. ;RVTLEN equ    $ - rivvvt
  124.  
  125.  
  126. handle    dw    0
  127. flag1    db    LOW(TRUE)        ;Find First flag
  128. verbose    db    PLAIN            ;verbose display switch
  129.                     ;PLAIN, VERBO, or MONOSYLLABIC
  130. znameptr dw    0            ;point past ZIP target file path
  131. mnameptr dw    0            ;remember .zFilename start
  132. dirNrEntry dw    0            ;central directory file count
  133. dirctr    dw    0            ;for counting down members
  134. pathflag db    ' '            ;is set to '+' if member
  135.                     ; filename includes a path
  136.  
  137. vhdrflag db    FALSE            ;set true when first member
  138.                     ;file is found
  139.  
  140. Comment        ~  Looks like:
  141. +filename.typ 000K / 000K  +filename.typ 000K / 000K  +filename.typ 000K / 000K
  142. Comment ends    ~
  143. blankline db    '         .       K /    K           .       K /    K  '
  144.       db    '         .       K /    K',CR,LF
  145. LINELEN    equ    $ - blankline
  146.  
  147. ;    Verbose display data
  148. ;    display lines for verbose
  149. ;    Adding a "E" (just before Stowage style) if encrypted
  150.  
  151. vhdr    db    CR,LF
  152.  db ' Name            Length E  Stowage     SF  Size now       Date   Time  CRC'
  153.  db CR,LF
  154.  db ' ============  ======== = ========   ====  ========  ========= ======  ========'
  155.  db CR,LF
  156. VHDRLEN equ    $ - vhdr
  157.  
  158. vline    label    byte            ;db    CR,LF
  159. vname    db    15 dup (' ')
  160. vlength    db     9 dup (' ')        ; length in archive
  161. vencflag db     3 dup (' ')        ;"E" if encrypted, else blank
  162. vstyle    db    10 dup (' ')        ; compression method (text)
  163. vfactor    db    ' xx%  '        ; compression factor (percentage)
  164. vsize    db    10 dup (' ')        ; actual file bytes
  165. vdate    db    'dd '            ; creation date
  166.  vmonth    db    'mmm '
  167.  vyear    db    'yy  '
  168.  vtime    db    'hh:mm  '        ; creation time
  169.  vcrc    db    'xxxxxxxx'        ; 32-bit crc in hex
  170.     db    CR,LF
  171. VLINELEN    equ    $ - vline
  172.  
  173.  
  174. ;    final totals line
  175.  
  176. vthdr    db    '*Total    '
  177. vtmbrs    db    5 dup (' ')
  178. vtlen    db    8 dup (' '),'  '
  179.     db    12 dup (' ')
  180.  vtsf    db    '   %  '
  181.  vtsize    db    8 dup (' ')
  182.     db    CR,LF            ; for tom
  183. VTHDRLEN    equ    $ - vthdr
  184.  
  185. ;Totals for each ZIP file's members:
  186.  
  187. totcmp    dw    0,0            ; total of file lengths
  188. totuncmp dw    0,0            ; total of file sizes
  189. totmbrs    dw    0            ; total number of files
  190. TOTLEN    equ    $ - totcmp
  191.  
  192. ;Totals for ALL ZIP files displayed:
  193.  
  194. ttotcmp    dw    0,0            ;total compressed file size
  195. ttotuncmp dw    0,0            ;total uncompressed file size
  196. ttotmbrs dw    0            ;total nr member files
  197.  
  198.  
  199. ;    ZIP compression types:
  200.  
  201. ;#define STORED            0    /* compression methods */
  202. ;#define SHRUNK            1
  203. ;#define REDUCED1          2
  204. ;#define REDUCED2          3
  205. ;#define REDUCED3          4
  206. ;#define REDUCED4          5
  207. ;#define IMPLODED          6
  208. ;#define TOKENIZED         7
  209. ;#define DEFLATED          8
  210. ;#define NUM_METHODS       9    /* index of last method + 1 */
  211.  
  212. MAXSTYLES    EQU    8    ;v1.8
  213. zstyles label    byte
  214.     db    '  Stored'    ;0 - The file is stored (no compression)
  215.     db    '  Shrunk'    ;1 - The file is Shrunk
  216.     db    'Reduced1'    ;2 - Reduced with compression factor 1
  217.     db    'Reduced2'    ;3 - Reduced with    "    "    2
  218.     db    'Reduced3'    ;4 - Reduced with    "    "    3
  219.     db    'Reduced4'    ;5 - Reduced with    "    "    4
  220.     db    'Imploded'    ;6 - The file is imploded
  221.     db    'Tokenize'    ;7 - The file is "tokenized"        v1.8
  222.     db    'Deflated'    ;8 - The file is Deflated        v1.8
  223.     db    ' Unknown'    ;illegal or unknown value
  224.  
  225. months    db    'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec '
  226.  
  227. ; translation table for upper-casing national chars:
  228. UpperSet    Db    'ÄÖÜÇÅÉÆÑ'    ; upper case national chars
  229. LowerSet    Db    'äöüçåéæñ'    ; lower case national chars
  230.  
  231.  
  232. Zdir    endp
  233.  
  234. Start    proc    near
  235.  
  236.     call    Parse_CmdLine            ;parse cmdline for
  237.                         ;target filenames
  238.     jb    Jmp_Msg_Term            ;failed
  239.  
  240. ;Let's get to work
  241.  
  242.     call    Find_Zip
  243.     jnc    ZipLup_73            ;found the first one
  244.      mov    dx,offset msg2            ;'Zipfile not found'
  245.      mov    cx,MSG2LEN
  246. Jmp_Msg_Term:
  247.      jmp    Msg_Term            ;display, terminate
  248.  
  249. ZipLup_73:
  250.     call    Read_CentralDir            ;try to read in file trailer
  251.     jnb    Got_Dir                ;found it, DS:BX -> directory
  252.  
  253.      mov    dx,offset msg3            ;'No central directory'
  254.      mov    cx,MSG3LEN
  255.      jmp    short Next1            ;right to next ZIP file
  256.  
  257. Got_Dir:
  258. ;    We'll only show our verbose header line
  259. ;    when and if we find our first member file.
  260. ;    (Down in Show_FileData)
  261.  
  262.     cmp    verbose,MONOSYL            ;mode switch
  263.     ja    MemberLup            ;verbose, no init
  264.     jb    GD_InitLine            ;plain, use standard
  265.  
  266.     mov    di,offset blankline        ;if monosyllabic, blank
  267.     mov    al,' '                ; that blankline for
  268.     mov    cx,LINELEN-2            ; good
  269.     rep    stosb
  270.  
  271. GD_InitLine:
  272.      call    Refresh_LineBuff        ;Init formatted display line
  273.  
  274. MemberLup:
  275.     cmp    word ptr [bx],4B50H        ;signature?
  276.     jnz    Bad_Dir                ;nope, bogus
  277.     cmp    word ptr 2[bx],0201H        ;normal entry?
  278.     jnz    Bad_Dir                ;nope, bogus
  279.  
  280. ;Normal directory entry.  Display it.
  281.  
  282.     call    Show_FileData            ;display entry info
  283.     dec    dirctr                    ;decr nr entries
  284.     jz    Next_Zip            ;last entry
  285.      jmp    MemberLup            ;next member
  286.  
  287.  
  288. Bad_Dir:
  289.     mov    dx,offset msg4            ;'ZIP is out of alignment'
  290.     mov    cx,MSG4LEN            ; or not a .ZIP file'
  291.     call    Pr_StdOut            ;display msg
  292.  
  293. Next_Zip:
  294.     mov    dx,offset crlf            ;need a new line
  295.     mov    cx,CRLFLEN            ;length
  296.     cmp    verbose,VERBO            ;verbose mode?
  297.     jz    Show_Verbose_Totals        ;yep, maybe display totals
  298.  
  299.     mov    ax,offset linebuff        ;formatted line start
  300.     cmp    di,ax                ;got any file entries?
  301.     jz    Next1                ;nope
  302.      dec    di                ;back up over the two spaces
  303.      dec    di                ; from the last file size
  304.      mov    dx,ax                ;DX'll need it for display
  305.      mov    cx,ax                ;CX will be nr chars
  306.      mov    ax,0A0DH            ;CR/LF
  307.      stosw                    ;stuff
  308.      stosw                    ;2 CR/LFs
  309.      xchg    cx,di                ;CX = end, DI = start
  310.      sub    cx,di                ;end - start = length
  311.      jmp    short Next1
  312.  
  313.  
  314. ;Verbose mode
  315.  
  316. Show_Verbose_Totals:
  317.     cmp    totmbrs,0            ;any totals?
  318.     jz    Next1                ;nope
  319.      call    Format_Totals            ;yep, format
  320.                         ;CX,DX prepared for ...
  321. ;    If just 1 member, there won't BE a total display!
  322. ;    BP returns from Format_Totals with 0 (1 file) or 1 (more than
  323. ;    1 file)
  324.     or    bp,bp                ;just 1 file?
  325.     jz    Next2                ;yep, no totals
  326.  
  327. Next1:
  328.     call    Pr_StdOut            ;display line seg or CR/LF
  329. Next2:
  330.     call    Find_Zip            ;next .ZIP file
  331.     jnc    ZipLup_73            ;found it, loop
  332.  
  333.     xor    ax,ax                ;handy 0/FALSE
  334.     cmp    al,verbose            ;nonverbose mode?
  335.     jle    NoMsg_Term            ;nonverbose, terminate
  336.                         ;(errorlevel 0)
  337.  
  338. ;    We've been accumulating overall totals.
  339. ;    Now display them.  Gotta move the total totals
  340. ;    into the totals (where Format_Totals expects them).
  341.  
  342. ;    If we never displayed our verbose header line,
  343. ;    we never found a qualifying member file!
  344. ;    Ergo .. no totals ..
  345.  
  346.     cmp    vhdrflag,al    ;FALSE        ;no header line?
  347.     jz    NoMsg_Term            ;no hdr, so no totals
  348.  
  349.     mov    ax,ttotmbrs            ;accumulated total mbrs
  350.     cmp    ax,dirNrEntry            ;just the one ZIP?
  351.     jz    No_More                ;yep, forget total totals
  352.     cmp    ax,1                ;<= 1?
  353.     jbe    No_More                ;yep, forget the total totals
  354.  
  355.     mov    si,offset ttotcmp        ;move total totals
  356.     mov    di,offset totcmp        ;into totals
  357.     mov    cx,TOTLEN SHR 1            ;nr words
  358.     rep    movsw
  359.  
  360.     call    Pr_CrLf                ;down extra line
  361.     call    Format_Totals            ;format overall totals
  362.     call    Pr_StdOut            ;display them
  363.  
  364. No_More:
  365. ;    Keith Petersen suggested this oughtta go .. Sigh ..
  366. ;    mov    dx,offset rivvvt
  367. ;    mov    cx,RVTLEN            ;fall thru to...
  368.     xor    ax,ax                ;ERRORLEVEL 0
  369.     jmp    short NoMsg_Term
  370.  
  371. Msg_Term:
  372.     push    ax                ;save errorlevel
  373.     call    Pr_StdOut            ;display error msg
  374.     pop    ax
  375. NoMsg_Term:
  376.     mov    ah,4Ch                ;terminate, AL = ERRORLEVEL
  377.     int    21h
  378.  
  379. Start    endp
  380.  
  381.  
  382. ;SUBROUTINE
  383.  
  384. Find_Zip    proc    near
  385.  
  386. ;We reset our DTA to the default PSP DTA each time through.
  387. ;The actual ZIP file open uses another one.
  388.  
  389.     mov    dx,80H                ;use PSP DTA
  390.     mov    ah,1AH                ;set DTA
  391.     int    21h
  392.  
  393. ;v1.7a    new code
  394.     mov    ah,4FH            ;assume not first time thru
  395.                     ;think how many bytes,
  396.                     ; machine cycles THIS saved!
  397.     cmp    flag1,0FFh        ;first time thru?
  398.     jne    Find_Done        ;nope
  399.  
  400. ;First time through
  401.     not    flag1            ;set to NOT first time thru
  402.  
  403.     mov    dx,offset ziptarget    ;DS:DX -> zip target name
  404.     xor    cx,cx            ;read-only
  405.     dec    ah        ;4EH    ;find first
  406.  
  407. Find_Done:
  408.     int    21h            ;hah - two bytes saved!
  409.     jb    FindZ_X            ;failed, return CF set
  410.  
  411. ;v1.7a    new code ends
  412.  
  413. Comment    ~   introduced in v1.7
  414.     Once DOS has found a file, we could apply our own matching
  415.     on top (DOS will maybe find MORE than we like, but never LESS).
  416.     Basically, that's another 'call MatchW'; remember to 'CLC' before
  417.     returning, so that we will be called again. - Just one problem:
  418.     We need our old name, as used to be in ziptarget; but that has
  419.     now been overwritten with the previous matching file name. So
  420.     we'd need to keep the old name somewhere... Too lazy to do it now.
  421. End of comment v1.7    ~
  422.  
  423.     call    Move_FileName            ;move name in after path
  424.                         ;to create full name for open
  425.  
  426. ;Found target file.  Announce, set new DTA, open it.
  427.  
  428.     mov    dx,offset msg1            ;'Zip file: '
  429.     mov    cx,MSG1LEN
  430.     call    Pr_StdOut
  431.     Print    ziptarget            ;'filename.zip'
  432.     call    Pr_CrLf                ;new line
  433.  
  434. ;We need a new DTA for the file open/read so we don't blow away
  435. ;the find first/find next stuff in the PSP DTA.
  436.  
  437.     mov    dx,offset dta1BA        ;DS:DX -> new DTA
  438.     mov    ah,1Ah                ;set DTA
  439.     int    21h
  440.  
  441.     mov    dx,offset ziptarget        ;DS:dx -> filename buffer
  442.     mov    ax,3D00H            ;open file, read only
  443.     int    21h
  444.     mov    handle,ax            ;save handle
  445. FindZ_X:
  446.     ret
  447. Find_Zip    endp
  448.  
  449.  
  450. Move_FileName    proc    near
  451.  
  452.     mov    si,offset PSP_DTA_Name        ;FCB #1 +1
  453.     mov    di,znameptr            ;pointer to after path
  454.     mov    cx,6                ;first 12 bytes
  455.     rep    movsw
  456.     movsb                    ;13th byte
  457.     ret
  458.  
  459. Move_FileName    endp
  460.  
  461.  
  462. ;Reads in file tailer.
  463. ;Then scans for the unique central directory signature.
  464. ;On success:
  465. ;    DS:BX -> central directory structure
  466. ;    readlen = nr bytes actually read
  467. ;    CF clear
  468. ;Else CF set for failure
  469.  
  470. Read_CentralDir proc    near
  471.  
  472.     xor    cx,cx                ;CX:DX = offset from end
  473.     xor    dx,dx                ;to very end
  474.     mov    bx,handle            ;file handle
  475.     mov    ax,4202H            ;move file pointer to end
  476.     int    21H                ;gets file size in DX:AX
  477.     jb    RCD_Close            ;seek failed
  478.  
  479. ;    Increased ENDOFS to read in more of the ZIP file's tail.
  480. ;    Original value wasn't enough to allow for huge comments.
  481.  
  482.     sub    ax,ENDOFS            ;back up psn.lo
  483.     jnb    Read1                ;ok, no problem
  484.      sub    dx,1                ;got a borrow, decr psn.hi
  485.      jnb    Read1                ;no problem
  486.       xor    ax,ax                ;sigh .. small file ..
  487.       xor    dx,dx                ; .. back to very start
  488. Read1:
  489.     mov    cx,dx                ;psn.hi
  490.     mov    dx,ax                ;psn.lo
  491.     mov    ax,4200H            ;now move from start
  492.     int    21H                ;CX:DX point to offset from end
  493.                         ;(big) or to start (small)
  494.     jb    RCD_Close            ;failed, close up
  495.  
  496.     mov    cx,ENDOFS            ;try to read this much
  497.     mov    dx,offset dirbuff        ;into our directory buffer
  498.     mov    di,dx                ;DI'll need it in a second
  499.     mov    ah,3FH                ;read from file/device
  500.     int    21H                ;AX=bytes read
  501.     jb    RCD_Close            ;failed, close up
  502.  
  503. ;First scan the end structure to locate the central directory.
  504.  
  505.     call    Sig_Scan            ;find the structure start
  506.                         ;(using bytes read in AX)
  507.     jb    RCD_InvalidDir            ;failed, close and exit
  508.  
  509. ;ES:DI -> end structure start (the signature)
  510. ;While we have the end structure, let's pick up and display
  511. ;any ZIP file comment.
  512.  
  513.     mov    cx,[di].zCmtLen            ;comment length
  514.     jcxz    RCD_NoComment            ;nope, forget it
  515.  
  516.      lea    dx,[di].zCmt            ;DS:DX -> comment
  517.      call    Pr_StdOut            ;display it.
  518.      call    Pr_CrLf                ;and a new line
  519.  
  520. RCD_NoComment:
  521.  
  522. ; Pick up the central directory file pointer (long integer):
  523. ; It's a pointer directly to the central directory start.
  524. ; This will only work for single-disk ZIP files.
  525.  
  526.     mov    si,[di].zDirSiz            ;save central directory size
  527.  
  528.     mov    ax,[di].zDirNrEntry        ;nr central dir entries
  529.     mov    dirNrEntry,ax            ;save it for later
  530.     mov    dirctr,ax            ;two places
  531.  
  532.     mov    dx,[di].zDirOfs            ;central directory offset.lo
  533.     mov    cx,[di].zDirOfs[2]        ;offset.hi
  534.     mov    ax,4200H            ;move file ptrs from start
  535.     int    21H
  536.     jb    RCD_Close            ;seek failed, close and exit
  537.  
  538.     mov    cx,si                ;read central dir size bytes
  539.     mov    dx,offset dirbuff        ;into our directory buffer
  540.     mov    ah,3FH                ;read from file/device
  541.     int    21H
  542.     jb    RCD_Close            ;failed, forget it
  543.  
  544.     mov    di,dx                ;DI -> dir start
  545.     xor    ax,ax                ;return AX=0 for ok
  546.     cmp    word ptr [di],4B50H        ;is it a signature?
  547.     jnz    RCD_InvalidDir            ;nope
  548.      cmp    word ptr 2[di],0201H        ;is it a file entry sig?
  549.      jz    RCD_Close            ;yep, good to go, CF clear
  550.  
  551. RCD_InvalidDir:
  552.     mov    al,11                ;Invalid format
  553.     stc                    ;insure CF set, AL=error
  554. RCD_Close:
  555.     mov    dx,ax                ;save error value (if any)
  556.     pushf                    ;save flags
  557.     mov    ah,3EH                ;close file
  558.     int    21H                ;(BX = file handle)
  559.     popf                    ;restore orig flags
  560.     mov    ax,dx                ;and any error value
  561.     mov    bx,di                ;if it went well,
  562.                         ;DS:BX -> central dir struc
  563.     ret
  564.  
  565.  
  566. ;    Subroutine for Read_CentralDir
  567. ;    Scans backwards through buffer for a directory end signature.
  568. ;    Enter with:
  569. ;    SI = second two signature bytes
  570. ;    DX -> buffer start
  571. ;    AX = bytes read
  572.  
  573. ;We must scan at the byte level (since a member or directory entry
  574. ;can be ANY length).
  575.  
  576. Sig_Scan:
  577.     mov    di,dx                ;ES:DI -> dirbuff
  578.                         ;(now a ZIP end dir structure)
  579.     add    di,ax                ;+ bytes read = -> buff end
  580.     mov    cx,ax                ;bytes read for the scan
  581.     inc    cx                ;debug for MAX size
  582.     inc    cx
  583.     mov    ax,4B50H            ;scan for first signature char
  584.                         ;(same for all structures)
  585.     mov    si,0605H            ;2d 2 chars of a directory
  586.                         ;end structure signature
  587.     std                    ;scan from end to start
  588.  
  589. SS_Lup:
  590.     repne    scasb                ;ES:DI -> read buffer
  591.     jnz    No_Sig                ;not found
  592.     jcxz    No_Sig                ;scanned it all
  593.  
  594. ;ES:DI -> the byte BEFORE the 50H signature
  595.  
  596.     cmp    2[di],ah            ;2d signature byte?
  597.     jnz    SS_Lup                ;nope, keep searching
  598.     cmp    3[di],si            ;last 2 signature bytes?
  599.     jnz    SS_Lup                ;nope, keep searching
  600.  
  601.     inc    di                ;bump from that last scasb
  602.                         ;DS:DI -> dir structure
  603.     cld                    ;forwards again to be neat
  604.     clc                    ;CF clear for success
  605.     ret
  606.  
  607. No_Sig:    cld                    ;forward again to be neat
  608.     stc                    ;return CF set for failure
  609.     ret
  610.  
  611. Read_CentralDir endp
  612.  
  613.  
  614. ;SUBROUTINE
  615. ;1 call
  616. Show_FileData    proc    near
  617. ;Added tests for cmdline member name testing
  618.  
  619.     call    Member_Test            ;see if file is eligible
  620.     jnz    SF_NextRec            ;nope, forget it
  621.  
  622.     push    bx
  623.  
  624.     xor    ax,ax                ;FALSE
  625.     cmp    al,verbose            ;not verbose?
  626.     jle    SFD_NonVerbose            ;not verbose
  627.  
  628. ;Verbose mode
  629.      cmp    vhdrflag,al    ;FALSE        ;verbose hdr displayed?
  630.      jnz    SFD_HdrDone            ;yep
  631.  
  632.      not    vhdrflag            ;set to TRUE
  633.      mov    dx,offset vhdr            ;display verbose header
  634.      mov    cx,VHDRLEN
  635.      call    Pr_StdOut
  636.  
  637. SFD_HdrDone:
  638.      call    Show_Verbose            ;show verbose member display
  639.      jmp    short SFD_BumpPtrs        ;skip nonverbose stuff
  640.  
  641.  
  642. ;Nonverbose mode
  643.  
  644. SFD_NonVerbose:
  645.     call    Stuff_FileName            ;parse, pad at ES:DI
  646.  
  647.     cmp    verbose,MONOSYL            ;monosyllabic output?
  648.     je    SFD_FlushLine            ;shove it out
  649.  
  650.     lea    si,[bx].zCmpSiz            ;compressed size (long int)
  651.     call    Stuff_FileSize            ;to ES:DI
  652.  
  653.     lea    si,[bx].zUncmpSiz        ;uncompressed size
  654.     inc    di                ;adjust line ptr
  655.     call    Stuff_FileSize            ;stuff uncompressed size
  656.                         ; to ES:DI
  657.     cmp    di,offset linebuff + LINELEN    ;hit end?
  658.     jb    SFD_BumpPtrs            ;nope
  659.  
  660. SFD_FlushLine:
  661.      mov    dx,offset linebuff        ;display the line
  662.      mov    cx,LINELEN            ;length
  663.      call    Pr_StdOut            ;display it
  664.      call    Refresh_LineBuff        ;refresh dynamic variable
  665.  
  666. SFD_BumpPtrs:
  667.     pop    bx
  668.  
  669. SF_NextRec:
  670. ;Now bump our BX pointer to the next entry
  671.     lea    ax,[bx].zFilename        ;from filename start
  672.     add    ax,[bx].zNameLen        ;add in name field length
  673.     add    ax,[bx].zExtraLen        ;and extra field length
  674.     add    ax,[bx].zFilCmtLen        ;and file comment field length
  675.     mov    bx,ax                ;should be next record
  676.     ret
  677.  
  678. Show_FileData    endp
  679.  
  680. ;Tests to see if THIS file is eligible (e.g., meets the ambiguous
  681. ;filename entered on cmdline at startup).
  682. ;  If no such parm, accept it (return CF clear).
  683. ;  If it matches, return CF clear.
  684. ;  If no match, return CF set.
  685.  
  686. ;While we're here, let's Asciify that stupid ZIP directory file name
  687. ;for later tests.
  688.  
  689. Member_Test    proc    near
  690.  
  691.     mov    cx,[bx].zNameLen        ;name length
  692.     jcxz    MT_Fail1            ;zero .. flunk it!
  693.  
  694.     push    di                ;save formatted line pointer
  695.  
  696.     lea    si,[bx].zFilename        ;dirEntry filename
  697.     mov    di,si                ;start
  698.     dec    di                ;back it up one
  699.     mov    ax,di                ;remember new start
  700.     mov    dx,cx                ;save length
  701.  
  702.     rep    movsb                ;move it left 1 char
  703.                         ;(make room for AsciiZ 0)
  704.     mov    byte ptr [di],0            ;AsciiZ the name
  705.  
  706.     mov    cx,dx                ;restore count/length
  707.     mov    si,ax                ;new start (1 char to left)
  708.     mov    dx,ax                ;remember in DX
  709.  
  710.     mov    mnameptr,ax            ;assume no paths
  711.  
  712.     cmp    byte ptr 1[si],':'        ;a drive separator?
  713.     jnz    MT_NoDrive            ;nope
  714.      mov    ax,2
  715.      add    si,ax                ;bump past d:
  716.      sub    cx,ax                ;adjust length counter
  717.      jbe    MT_Fail                ;zeroed out, flunk it!
  718.  
  719. MT_NoDrive:
  720.  
  721. ;Check for directory slashes
  722.     mov    di,si                ;starting point
  723. ;PKZIP uses the '/' character for paths!
  724. ;    Wonder why GWS is loading the entire AX?
  725. ;    We're only doing a SCASB for the slash!
  726. ;    Aha!  Because of that je MT01 below .. tricky, tricky...
  727.     mov    ax,' /'                ;scan for PKZIP
  728.                         ; directory slashes
  729. MT_SlashScan:
  730.     repne    scasb
  731.     jnz    MT_NoSlash            ;none
  732.      jcxz    MT_Fail                ;zeroed out, flunk it!
  733.      mov    si,di                ;new starting point
  734.      mov    byte ptr [di-1], '\'        ;old habits never die
  735.      jmp    MT_SlashScan            ;and try again until all gone
  736.  
  737. MT_NoSlash:
  738.  
  739. ;SI now points at first filename char.
  740.  
  741.     cmp    verbose,MONOSYL            ;are we monosyllabic?
  742.     je    MT_01                ;yes,remember full path
  743.  
  744.     mov    mnameptr,si            ;remember the new address
  745.  
  746. ;    We'll set a global flag if there was a path.
  747. ;    Simpler than testing if mnameptr = filename start
  748. ;    in both verbose and nonverbose display modes.
  749. ;    DX -> shifted .zFilename
  750. ;    SI -> filename first char
  751.  
  752.     mov    ax,'+ '                ;assume yes (no paths)
  753.                         ;(AL=' ',AH='+')
  754.     cmp    dx,si                ;name^ = zFileName^?
  755.     jz    MT_1                ;yep, no path
  756. MT_01:                        ;if monosyl, use ' '!
  757.      mov    al,ah    ;'+'            ;set the path flag
  758. MT_1:
  759.     mov    pathflag,al            ;post path flag
  760.  
  761.     cmp    byte ptr pname1,0        ;empty member name?
  762.     jz    MT_Done                ;empty, forget the compares
  763.  
  764.     mov    di, si
  765.     mov    si, offset pname1
  766.     call    MatchW
  767.  
  768.     pop    di
  769.     ret
  770.  
  771. MT_Fail:                    ;no match
  772.     pop    di                ;restore formatted line ptr
  773.  
  774. MT_Fail1:
  775.     mov    al,1                ;return ZF clear
  776. MT_X:    ret
  777.  
  778. MT_Done:
  779.     pop    di
  780.     xor    al,al                ;return ZF set for success
  781.     ret
  782.  
  783. Member_Test    endp
  784.  
  785. ; The following routine introduced in v1.7:
  786.  
  787. MatchW    Proc    Near
  788.  
  789.     ;    This function checks if a given string matches a given pattern
  790.     ;    including generalized wildcards, e.g., 'a*b?c'. The matching is
  791.     ;    case-sensitive. The result is returned in the flags:
  792.     ;       ZF set          = exact match  (check with  JZ  ExactMatch)
  793.     ;       CF set          = no match     (          JB  NoMatch)
  794.     ;       CF clear, ZF clear = prefix match (          JA  PartialMatch)
  795.     ;                         (or      JAE PartialOrExact)
  796.     ;
  797.     ;    Hand-assembly from a Fortran routine by Gunter Rademacher
  798.     ;
  799.     ;    Input
  800.     ;        si - pointer to pattern, ASCIIZ
  801.     ;        di - pointer to string,  ASCIIZ
  802.     ;    Output
  803.     ;        carry and zero flags (cf. above)
  804.     ;    Registers modified
  805.     ;        ax, cx, si, di
  806.  
  807.  
  808. ;    Let's use BX and DX (saving them on the stack just in case
  809. ;    they need to be preserved) instead of the variables saveSI and saveDI.
  810. ;    Gain speed, reduce size.
  811.  
  812.         push    bx            ;preserve
  813.         push    dx
  814.  
  815.         mov    dx,di            ;save DI a sec
  816.         xor    ax,ax            ;clear lsb and msb
  817.                         ;(to find end of AsciiZ
  818.                         ; string, later as flag)
  819.  
  820. ;    Bad assumption here:  What is CX prior to this REPNE SCASB?
  821. ;    Donno .. so we'd better make sure it's big, ne?
  822. ;    I can live with assuming we're already CLD'ed (forward)
  823. ;    I also don't see where saveSI was ever initialized!
  824. ;    I can only assume pattern start (since I don't quite have the
  825. ;    logic of this sucker figured out yet!).
  826.  
  827.         mov    bx,si            ;initialize saveSI
  828.  
  829.         mov    cx,0FFFFH        ;max number
  830.         Repne    Scasb
  831.         Dec    di
  832.         Dec    di
  833.         xchg    di,dx            ;DI restored (first char)
  834.                         ;DX=pointer to last char
  835. ;    AH is already 0 (possible return code)
  836.  
  837.         Xor    cx, cx            ; wildcard pointer
  838.  
  839. GetPat:        Lodsb                ; get char from pattern
  840.         Or    al, al
  841.         Jz    ChkMore            ; branch if end of pattern
  842.  
  843.         Cmp    al, STRINGWILD    ;'*'
  844.         Je    SaveStar        ; branch if string wildcard
  845.         Cmp    Byte Ptr [di], 0
  846.         Je    MatchWr            ; branch if at end of string
  847.  
  848.         Scasb                ; branch if pattern char matches
  849.         Je    GetPat            ; string wildcard
  850.         Cmp    Al, CHARWILD    ;'?'
  851.         Je    GetPat            ; ... or if char wildcard
  852.  
  853. MatchAny:    Dec    cx
  854.         inc    dx            ; restore last psn for
  855.         mov    di,dx            ; string wildcard + 1
  856.         Cmp    Byte Ptr [di], 0
  857.         Je    MatchWr
  858.          mov    si,bx            ; restore corresponding
  859.                         ; pattern
  860.          Jmp Short GetPat        ; ... position and loop
  861.  
  862. SaveStar:    Cmp    Byte Ptr [si], 0
  863.         Jz    Found            ; branch if end of pattern
  864.          Mov    cx, 1            ; mark this fact
  865.          mov    dx,di            ;save pointer to string
  866.          mov    bx,si            ;and pointer to pattern
  867.          Jmp Short GetPat        ; and loop
  868.  
  869. ChkMore:    Mov    ah, 2            ; at least substring match
  870.         Cmp    Byte Ptr [di], 0
  871.         Jne    MatchAny        ; loop if not at end of string
  872.  
  873. Found:        Mov    ah, 1            ; true match!
  874.  
  875. MatchWr:    Sub    ah, 1            ; done; set up flags
  876.  
  877.         pop    dx            ;restore
  878.         pop    bx
  879.         Ret
  880.  
  881. MatchW        EndP
  882.  
  883. ; End of routine introduced in
  884.  
  885.  
  886. ;ES:DI -> next position on formatted line
  887. ;zFilename is now AsciiZed
  888. ;mnameptr contains ptr to original or real zFilename start
  889. ;(paths stripped)
  890.  
  891. Stuff_FileName    proc    near
  892.  
  893.     push    di                ;save formatted line ptr
  894.  
  895. ;    Now using pathflag if target filename has a path
  896.  
  897.     mov    al,pathflag            ;' ' if no paths,
  898.                         ;'+' if paths
  899.     mov    si,mnameptr            ;ptr to filename start
  900.  
  901.     stosb                    ;stuff space or '+'
  902.     mov    dx,di                ;name start
  903.     add    dx,8                ;bump to the dot
  904.  
  905. SN_Lup:    lodsb
  906.     or    al,al                ;hit AsciiZ 0 yet?
  907.     jz    SN_Done                ;yep
  908.     cmp    verbose,MONOSYL            ;monosyllabic?
  909.     je    SN_NoDot            ;ay,don't mess with '.'
  910.     cmp    al,'.'                ;.typ separator?
  911.     jnz    SN_NoDot            ;nope
  912.      cmp    di,dx                ;where the dot should go?
  913.      jz    SN_NoDot            ;yep, put it there
  914.      mov    di,dx                ; bump to the dot psn
  915. SN_NoDot:
  916.     stosb                    ;stuff in formatted line
  917.     jmp    SN_Lup
  918.  
  919. SN_Done:
  920.     pop    di                ;orig ptr
  921.     add    di,14                ;bump to size psn
  922.     ret
  923.  
  924. Stuff_FileName    endp
  925.  
  926.  
  927. ;SUBROUTINE
  928. ;2 calls
  929.  
  930. Stuff_FileSize    proc    near
  931.     push    bx                ;Preserve BX! (dta ptr)
  932.  
  933.     add    di,3                ;move to number string end
  934.     push    di                ;save it (ptr to last digit)
  935.  
  936.     mov    ax,[si]
  937.     mov    dx,[si+2]
  938.     add    ax,3FFh                ;div 1024 to get Kb
  939.     adc    dx,0
  940.     mov    cl,0Ah
  941.     shr    ax,cl
  942.     mov    cl,6
  943.     shl    dx,cl
  944.     add    ax,dx
  945.  
  946.     mov    si,0Ah
  947. SFS_Lup:
  948.     xor    dx,dx
  949.     div    si
  950.     add    dl,30H                ;asciify
  951.     dec    di                ;back up the digit pointer
  952.     mov    [di],dl                ;stuff digit in buffer
  953.     or    ax,ax                ;number done?
  954.     jnz    SFS_Lup                ;nope
  955.  
  956.     pop    di                ;restore line buffer ptr
  957.     add    di,3                ;bump past ' / ' or ' | '
  958.     pop    bx
  959.     ret
  960.  
  961. Stuff_FileSize    endp
  962.  
  963.  
  964. ;Verbose display functions
  965. ;Format, display single line for each member
  966. ;On success, return:
  967. ; CF clear
  968. ; AL = 0
  969. ;On error, return:
  970. ; CF set (because of output write fail)
  971. ; AL = error code
  972. ;Preserve BX (buffer ptr)
  973. ;    Right before the compression style:
  974. ;    "E" if encrypted
  975. ;    Else blank
  976. ;    Forgot to show our possible "extended" filename with path
  977. ;    (via a "+" preceding the stripped filename).
  978. ;    Worked fine for nonverbose mode, now adding for verbose mode.
  979. sign    db    ' '                ;local variable
  980. hundred    dw    100                ; for computing percentages
  981.  
  982.  
  983. Show_Verbose    proc    near
  984.  
  985.     mov    si,mnameptr            ;move real member name
  986.                         ;(no paths)
  987.     mov    di,offset vname            ;into formatted line
  988.     mov    al,pathflag            ;' ' if no paths,
  989.                         ;'+' if paths
  990.     stosb                    ;stuff '+'
  991.     mov    cx,13                ;14 spaces in field
  992.                         ; minus the path flag
  993.                         ; already stuffed
  994. SV_Lup:
  995.     lodsb                    ;snarf char
  996.     or    al,al                ;AsciiZ ending?
  997.     jz    SV_5                ;yep
  998.      stosb
  999.      loop    SV_Lup
  1000.  
  1001. SV_5:
  1002. ;    I don't see where we EVER loaded AH with a space!
  1003. ;    Must have lost it in one of the hacks.
  1004. ;    Replacing it again.
  1005.  
  1006.     mov    al,20H
  1007.     rep    stosb                ;pad with spaces
  1008.  
  1009. ; reduce the size/length to word values
  1010.  
  1011.     mov    si,[bx].zUncmpSiz        ; get uncompressed file size
  1012.     mov    ax,[bx].zUncmpSiz[2]
  1013.  
  1014.     mov    cx,[bx].zCmpSiz            ;compressed size
  1015.     mov    dx,[bx].zCmpSiz[2]
  1016.  
  1017. SVL_51:    or    ax,ax                ; big number?
  1018.     jz    SV_52                ; nope, can use it
  1019.      shr    ax,1                ; yup, divide by two
  1020.      rcr    si,1
  1021.      shr    dx,1
  1022.      rcr    cx,1
  1023.      jmp    SVL_51                ;loop
  1024.  
  1025. SV_52:
  1026.     mov    ax,si                ; low word of actual size
  1027.     mov    sign,' '
  1028.     cmp    ax,cx                ; arc member is larger?
  1029.     jb    SV_520
  1030.      sub    ax,cx                ; amount saved
  1031.      jmp    short SV_521
  1032.  
  1033. SV_520:
  1034.     sub    ax,cx
  1035.     neg    ax
  1036.     mov    sign,'-'
  1037.  
  1038. SV_521:
  1039.     mul    hundred                ; to percentage
  1040.     add    ax,50
  1041.  
  1042. ;    I'm thinking PK isn't doing this rounding ..
  1043. ;    our percentage figures are sometimes 1% off the PKZIP -v display.
  1044. ;    Close enough for govt work...
  1045.  
  1046.     adc    dx,0                ; round up percent
  1047.     or    si,si                ; empty file?
  1048.     jnz    SV_53
  1049.      mov    ax,100
  1050.      jmp    short SV_54
  1051.  
  1052. SV_53:    div    si
  1053. SV_54:    cmp    ax,100                ; archive fouled?
  1054.     jbe    SV_55
  1055.      sub    ax,ax
  1056. SV_55:
  1057.     mov    di,offset vfactor-2        ;format stowage factor
  1058.     call    Asciify                ;display AX
  1059.  
  1060.     mov    al,sign
  1061.     mov    vfactor,al
  1062.  
  1063. ;    If encrypted, stuff an 'E', else a blank
  1064.     mov    di,offset vencflag        ;space for "E", space
  1065.     mov    ax,' E'                ;assume unencrypted
  1066.                         ;(AL='E',AH=' ')
  1067.     test    byte ptr [bx].zBitFlag,1    ;If 0 bit is set,
  1068.     jnz    SV_56                ;it's encrypted
  1069.      mov    al,ah    ;' '            ;blank (not encrypted)
  1070. SV_56:
  1071.     stosw                    ;stuff 'E' or space,
  1072.                         ;trailing space
  1073.                         ;DI -> vstyle field
  1074.  
  1075. ;    Adding test to insure compression method (0..6) is in legal range
  1076. ;    (e.g., doesn't overrun our compression style table)
  1077.  
  1078.     mov    si,offset zstyles        ;style table start
  1079.     xor    ax,ax                ;clear msb
  1080.     or    ax,[bx].zCmpMeth        ;bring in method
  1081.                         ; of compression
  1082.     jz    SV_58                ;0 -> use table base
  1083.  
  1084. ;v1.8    cmp    al,6                ;max legal
  1085.     cmp    al,MAXSTYLES            ;max legal        v1.8
  1086.     jbe    SV_57                ;it's legal
  1087. ;v1.8     mov    al,7                ;' Unknown'
  1088.      mov    al,MAXSTYLES+1            ;' Unknown'        v1.8
  1089. SV_57:
  1090.     mov    cl,3                ; eight bytes each entry
  1091.     shl    ax,cl
  1092.  
  1093. SV_58:
  1094.     add    si,ax                ;table base + offset
  1095.                         ;DI already points to vstyle
  1096.  
  1097.     mov    cx,4                ;move as words (8 bytes)
  1098.     rep    movsw
  1099.  
  1100.     mov    dx,[bx].zCmpSiz[2]        ;compressed size.hi
  1101.     mov    ax,[bx].zCmpSiz            ;compressed size
  1102.     add    totuncmp,ax            ;accumulate
  1103.     adc    totuncmp[2],dx
  1104.     mov    di,offset vsize            ;format file size
  1105.     call    Asciify_Long
  1106.  
  1107.     mov    dx,[bx].zUncmpSiz[2]        ;uncompressed size.hi
  1108.     mov    ax,[bx].zUncmpSiz        ;uncompressed size.lo
  1109.     add    totcmp,ax            ;accumulate
  1110.     adc    totcmp[2],dx
  1111.     mov    di,offset vlength        ;format file length
  1112.     call    Asciify_Long
  1113.  
  1114.     mov    ax,[bx].zModDate        ; format file date
  1115.     call    GetDate
  1116.  
  1117.     mov    ax,[bx].zModTime        ; format file time
  1118.     call    GetTime
  1119.  
  1120.     mov    ax,[bx].zCrc32            ; format crc.lo in hex
  1121.     mov    di,offset vcrc + 4
  1122.     call    Cvh
  1123.     mov    ax,[bx].zCrc32[2]        ; format crc.hi in hex
  1124.     mov    di,offset vcrc
  1125.     call    Cvh
  1126.  
  1127.     inc    totmbrs                ;bump total file count
  1128.  
  1129.     mov    dx,offset vline            ;display formatted info
  1130.     mov    cx,VLINELEN
  1131.     call    Pr_StdOut
  1132.     ret
  1133.  
  1134. Show_Verbose    endp
  1135.  
  1136.  
  1137. ;Formats, displays verbose totals
  1138.  
  1139. Format_Totals    proc    near
  1140.  
  1141.     mov    ax,totmbrs            ;total members
  1142.     add    ttotmbrs,ax            ;accumulate
  1143.  
  1144. ;    Don't display totals for this ZIP file's members
  1145. ;    unless there's more than 1.
  1146.  
  1147.     xor    bp,bp                ;use BP for a flag
  1148.                         ;(undisturbed by Asciify)
  1149.                         ;(0 = only 1 file)
  1150.  
  1151.     cmp    ax,1                ;just one?
  1152.     jbe    FT_1                ;yep, no Asciify
  1153.      inc    bp                ;flag more than 1 file
  1154.      mov    di,offset vtmbrs-2        ;format total members
  1155.      call    Asciify
  1156.  
  1157. FT_1:    mov    dx,totcmp[2]            ;total compressed file size
  1158.     mov    ax,totcmp
  1159.     add    ttotcmp,ax            ;accumulate total totals
  1160.     adc    ttotcmp[2],dx
  1161.  
  1162.     or    bp,bp                ;just 1 file?
  1163.     jz    FT_2                ;yep, no Asciify
  1164.      mov    di,offset vtlen            ;format total compressed file
  1165.                         ;size
  1166.      call    Asciify_Long
  1167. FT_2:
  1168.     mov    dx,totuncmp[2]            ; total uncompressed file size
  1169.     mov    ax,totuncmp
  1170.     add    ttotuncmp,ax            ;accumulate total totals
  1171.     adc    ttotuncmp[2],dx
  1172.     or    bp,bp                ;just 1 file?
  1173.     jz    FT_9                ;yep, no Asciify
  1174.                         ;no fancy computations
  1175.  
  1176.     mov    di,offset vtsize        ;format total uncompressed
  1177.                         ;file size
  1178.     call    Asciify_Long
  1179.  
  1180. ; reduce the total size/length to word values
  1181.  
  1182.     mov    si,totcmp            ; get compressed file size
  1183.     mov    ax,totcmp[2]
  1184.     mov    cx,totuncmp            ; uncompressed file size
  1185.     mov    dx,totuncmp[2]
  1186.  
  1187. FTDiv:    or    ax,ax                ; big number?
  1188.     jz    FT_4                ; nope, can use it
  1189.      shr    ax,1                ; yup, divide by two
  1190.      rcr    si,1
  1191.      shr    dx,1
  1192.      rcr    cx,1
  1193.      jmp    short FTDiv
  1194.  
  1195. FT_4:
  1196.     mov    ax,si
  1197.     mov    sign,' '            ;whata kludge
  1198.     cmp    ax,cx                ;compressed > uncompressed?
  1199.     jb    FT_5                ;yep
  1200.      sub    ax,cx                ;amount saved
  1201.      jmp    short FT_6
  1202.  
  1203. FT_5:    sub    ax,cx
  1204.     neg    ax
  1205.     mov    sign,'-'
  1206.  
  1207. FT_6:    mul    hundred                ; to percentage
  1208.     add    ax,50
  1209.     adc    dx,0                ; round up percent
  1210.     or    si,si                ; empty file?
  1211.     jnz    FT_7
  1212.      mov    ax,100
  1213.      jmp    short FT_8
  1214.  
  1215. FT_7:    div    si
  1216. FT_8:    mov    di,offset vtsf-2        ;format stowage factor
  1217.     call    Asciify                ;AX
  1218.  
  1219.     mov    al,sign
  1220.     mov    vtsf,al
  1221.  
  1222. FT_9:
  1223.     mov    di,offset totcmp        ;starting at totcmp
  1224.     mov    cx,TOTLEN/2            ;length of totals to clear
  1225.                         ; (words)
  1226.     xor    ax,ax                ;handy 0
  1227.     rep    stosw
  1228.  
  1229.     or    bp,bp                ;just 1 file?
  1230.     jz    FT_X                ;yep, exit
  1231.  
  1232.      mov    dx,offset vthdr            ;prepare to display totals
  1233.      mov    cx,VTHDRLEN            ;msg length
  1234. FT_X:
  1235.     ret                    ;to display
  1236.  
  1237. Format_Totals    endp
  1238.  
  1239.  
  1240. ;    format the time (in AX)
  1241.  
  1242. time    record    hour:5,min:6,sec:5        ;packed time
  1243.  
  1244. GetTime proc    near                ;format the date
  1245.     mov    di,offset vtime
  1246.     or    ax,ax                ;it is zero?
  1247.     jz    GotTime
  1248.  
  1249.     push    ax                ;save date
  1250.     and    ax,mask hour            ;get hour part
  1251.     mov    cl,hour                ;bits to shift
  1252.     shr    ax,cl
  1253.     call    Cnvrt1
  1254.     stosw
  1255.     mov    al,':'
  1256.     stosb
  1257.  
  1258. GT3:    pop    ax                ;get the time back
  1259.     and    ax,mask min            ;get min part
  1260.     mov    cl,min                ;bits to shift
  1261.     call    Cnvrt
  1262.     stosw
  1263. GotTime:ret
  1264.  
  1265. GetTime endp
  1266.  
  1267.  
  1268. Cnvrt2    proc    near                ;convert to ascii
  1269.  
  1270.     call    Cnvrt
  1271.     cmp    al,'0'                ;suppress leading zero
  1272.     jne    Cnvrtd
  1273.      mov    al,' '
  1274.      ret
  1275.  
  1276. Cnvrt:    shr    ax,cl
  1277. Cnvrt1:    aam                    ;make al into bcd
  1278.     or    ax,'00'                ; and to ascii
  1279.     xchg    al,ah
  1280. Cnvrtd:    ret
  1281. Cnvrt2    endp
  1282.  
  1283.     page
  1284.  
  1285. ;    format the date (in AX)
  1286.  
  1287. date    record    yr:7,mo:4,dy:5            ;packed date
  1288.  
  1289. GetDate proc    near                ;format the date
  1290.     or    ax,ax                ;is it zero?
  1291.     jz    GotDate
  1292.  
  1293.     push    bx                ;preserve BX (buff ptr)
  1294.  
  1295.     push    ax                ;save date
  1296.     and    ax,mask yr            ;get year part
  1297.     mov    cl,yr                ;bits to shift
  1298.     call    Cnvrt
  1299.     mov    di,offset vyear
  1300.     or    al,'8'                ;adjust for base year
  1301.     stosw
  1302.  
  1303.     pop    bx                ;get the date back
  1304.     push    bx                ;save it
  1305.     and    bx,mask mo            ;get month part
  1306.     mov    cl,mo                ;bits to shift
  1307.     shr    bx,cl
  1308.     add    bx,bx                ; form month table index
  1309.     add    bx,bx
  1310.     lea    si,word ptr months-4[bx]
  1311.     mov    di,offset vmonth
  1312.     movsw                    ;2 bytes
  1313.     movsb                    ;the 3rd
  1314.  
  1315.     pop    ax                ;get the date back
  1316.     and    ax,mask dy            ;get day part
  1317.     mov    cl,dy                ;bits to shift
  1318.     call    Cnvrt
  1319.     mov    di,offset vdate
  1320.     stosw
  1321.  
  1322.     pop    bx                ;restore buff ptr
  1323. GotDate:ret
  1324.  
  1325. GetDate endp
  1326.  
  1327.  
  1328. ;A severely hacked single/double precision number conversion function.
  1329. ;Originally from JMODEM, but severely hacked by Toad Hall.
  1330. ;ES:DI -> string
  1331. ;Destroys everything almost.
  1332.  
  1333. ;Enter here if integer in AX
  1334. Asciify proc    near
  1335.  
  1336.     push    bx                ;save buff ptr
  1337.  
  1338.     xor    dx,dx                ; clear fake long.hi
  1339.     mov    si,ax                ;move integer into SI
  1340.     xor    ah,ah                ;clear msb (flag)
  1341.     jmp    short Ascii_Ax            ;jump into the code
  1342.  
  1343. ;Enter here if long integer in DX:AX.
  1344. Asciify_Long:
  1345.  
  1346.     push    bx                ;save buff ptr
  1347.  
  1348.     mov    si,ax                ;move long.lo into SI
  1349.     xor    ah,ah                ;clear msb (flag)
  1350.  
  1351. Comment        ~
  1352. Taking out the extremely high numbers to reduce column width.
  1353.  
  1354.     MOV    CX,3B9AH            ; Get billions
  1355.     MOV    BX,0CA00H
  1356.     CALL    Subtr                ; Subtract them out
  1357.  
  1358.     MOV    CX,05F5H            ; Get hundred-millions
  1359.     MOV    BX,0E100H
  1360.     CALL    Subtr                ; Subtract them out
  1361. Comment ends    ~
  1362.  
  1363.     and    dx,4FFH                ;seems likely
  1364.     MOV    CX,0098H            ; Get ten-millions
  1365.     MOV    BX,9680H
  1366.     CALL    Subtr                ; Subtract them out
  1367.  
  1368.     MOV    CX,000FH            ; Get millions
  1369.     MOV    BX,4240H
  1370.     CALL    Subtr                ; Subtract them out
  1371.  
  1372.     MOV    CX,1                ; Get hundred-thousands
  1373.     MOV    BX,86A0H
  1374.     CALL    Subtr                ; Subtract them out
  1375.  
  1376. Ascii_Ax:
  1377.     xor    cx,cx                ; Get ten-thousands
  1378.     MOV    BX,2710H
  1379.     CALL    Subtr                ; Subtract them out
  1380.     MOV    BX,03E8H
  1381.     CALL    Subtr                ; Subtract them out
  1382.  
  1383.     MOV    BX,0064H
  1384.     CALL    Subtr                ; Subtract them out
  1385.     MOV    BX,10
  1386.     CALL    Subtr                ; Subtract them out
  1387.     mov    ax,si                ;residual in SI
  1388.     add    AL,'0'                    ; Add bias to residual
  1389.     stosb                    ; Put in the string
  1390.  
  1391.     pop    bx                ;restore buff ptr
  1392.     RET
  1393.  
  1394. ;Common subroutine for Asciify
  1395.  
  1396. Subtr:    mov    al,'0'-1
  1397.  
  1398. Subtr1:    INC    al                ; Bump the digit character
  1399.     SUB    si,BX                ; Dword subtraction
  1400.     SBB    DX,CX
  1401.     JNB    Subtr1                ; Continue until a carry
  1402.  
  1403.     ADD    si,BX                ; One too many, add back
  1404.     ADC    DX,CX                ;   and the remainder
  1405.  
  1406.     cmp    al,'0'
  1407.     jnz    Subtr2                ;nope, turn off leading flag,
  1408.                         ; stuff
  1409.      or    ah,ah                ;no more leading spaces?
  1410.      jnz    Sub_Stuff            ;right, stuff the '0'
  1411.       mov    al,' '                ;make it neat
  1412.                         ; with leading spaces
  1413. Sub_Stuff:
  1414.     stosb                    ;stuff the char
  1415.     RET
  1416.  
  1417. Subtr2:    inc    ah                ;turn off leading space flag
  1418.     stosb
  1419.     ret
  1420.  
  1421. Asciify ENDP
  1422.  
  1423.  
  1424. ;Convert 16-bit binary word in AX
  1425. ;to hex ASCII string at ES:DI
  1426. ;(Protect BX, directory array pointer)
  1427.  
  1428. hexchar    db    '0123456789ABCDEF'
  1429.  
  1430. Cvh    proc    near
  1431.  
  1432.     push    bx                ;save buff ptr
  1433.  
  1434.     mov    si,offset hexchar        ;for faster access
  1435.     mov    dx,ax                ; save 16-bits
  1436.  
  1437.     mov    bl,dh                ; third nibble
  1438.     xor    bh,bh                ;clear msb
  1439.     mov    cx,0F04H            ;CL=4 for shifting,
  1440.                         ;CH=0FH for masking
  1441.     shr    bl,cl
  1442.     mov    al,[si][bx]            ;snarf hex char
  1443.     stosb
  1444.  
  1445.     mov    bl,dh                ; last nibble
  1446.     and    bl,ch    ;0fh
  1447.     mov    al,[si][bx]            ;snarf hex char
  1448.     stosb
  1449.  
  1450.     mov    bl,dl                ; first nibble
  1451.     shr    bl,cl                ; isolate (CL still 4)
  1452.     mov    al,[si][bx]            ;snarf hex char
  1453.     stosb
  1454.  
  1455.     mov    bl,dl                ; second nibble
  1456.     and    bl,ch    ;0fh            ; isolate
  1457.     mov    al,[si][bx]            ;snarf hex char
  1458.     stosb
  1459.  
  1460.     pop    bx                ;restore buff ptr
  1461.     ret
  1462.  
  1463. Cvh    endp
  1464.  
  1465.  
  1466. ;SUBROUTINE
  1467.  
  1468. Pr_CrLf proc    near
  1469.     mov    dx,offset crlf
  1470.     mov    cx,CRLFLEN            ;fall through to ...
  1471.  
  1472. Pr_CrLf endp
  1473.  
  1474. Pr_StdOut    proc    near
  1475.  
  1476.     push    bx                ;preserve bx
  1477.     mov    bx,STDOUT
  1478.     mov    ah,40H                ;write
  1479.     int    21H
  1480.     pop    bx
  1481.     ret
  1482.  
  1483. Pr_StdOut    endp
  1484.  
  1485.  
  1486. ;Print null-terminated (AsciiZ) string like int 21h function 9
  1487. ;Enter with DS:DX -> AsciiZ string
  1488. ;Destroys AX
  1489. ;On success, return:
  1490. ; CF clear
  1491. ; AL = 0
  1492. ;On failure (StdOut write fail), return:
  1493. ; CF set
  1494. ; AL = error
  1495.  
  1496. PrintS    proc    near
  1497.  
  1498.     mov    cx,0FFFFH            ;max scan
  1499.     xor    al,al                ;handy 0
  1500.     mov    di,dx                ;string start
  1501.     repne    scasb                ;find the terminator
  1502.     inc    cx                ;adjust
  1503.     not    cx                ;CX=length
  1504.  
  1505.     call    Pr_StdOut            ;display to StdOut
  1506.  
  1507.     ret
  1508.  
  1509. PrintS    endp
  1510.  
  1511.  
  1512. ;Reinit our nonverbose formatted display line
  1513. ;Returne ES:DI -> formatted line start
  1514. Refresh_LineBuff    proc    near
  1515.  
  1516.     mov    si,offset blankline        ;formatted display line
  1517.     mov    ax,offset linebuff        ;dynamic data area
  1518.     mov    di,ax                ;move to ...
  1519.     mov    cx,LINELEN            ;length
  1520.     rep    movsb
  1521.     mov    di,ax                ;ES:DI -> formatted line start
  1522.     ret
  1523.  
  1524. Refresh_LineBuff    endp
  1525.  
  1526.  
  1527. ;Parses cmdline for target files.
  1528. ;If failure:
  1529. ;  Returns CF set,
  1530. ;  AL = ERRORLEVEL,
  1531. ;  DX -> error msg
  1532. ;Adding command line switch ('-v') parsing.
  1533. ;And '-m', too. Also accept '/' as switch char.
  1534. ;And fixing the bug.
  1535.  
  1536. Parse_CmdLine    proc    near
  1537.  
  1538.     call    _Args                ;parse cmdline
  1539.                         ;returns AX = argc
  1540.     or    ax,ax                ;any argc?
  1541.     jnz    Got_Parm            ;yep
  1542.  
  1543. Cmd_Err:
  1544.      mov    dx,offset usage            ;intro, usage
  1545.      mov    cx,USAGELEN            ;total length
  1546.      mov    al,1                ;errorlevel 1
  1547.      stc                    ;CF set
  1548.      ret                    ;for a jmp to Msg_Term
  1549.  
  1550. Got_Parm:
  1551.     mov    cx,ax                ;argc
  1552.  
  1553. ;See if any of the argv's are our '-v' verbose switch.
  1554. ;Or '-m'. Or '/v'. Or '/m'. Or. Or. Or.
  1555. ;If so, turn switch on, clear that argv.
  1556.  
  1557.     cmp    cx,1                ;just 1 arg?
  1558.     jz    Chk_ZipName            ;yep, can't be any switches
  1559.  
  1560.     mov    bx,offset argv[2]        ;start with ^argv(1)
  1561.  
  1562. Chk_VSwitch:
  1563.     mov    si,[bx]                ;argv^(1)
  1564.     cmp    byte ptr 2[si],0        ;arg longer than 2 chars?
  1565.     jnz    Chk_VRelup            ;yes, can't be switch
  1566.     mov    ax,[si]                ;snarf possible -V parm
  1567.     cmp    al,'-'                ;is first(!) char '-'?
  1568.     je    Found_Some_Switch        ;yes, check which
  1569.     cmp    al,'/'                ;is first(!) char '/'?
  1570.     jne    Chk_VRelup            ;no, cant' be switch
  1571.  
  1572. Found_Some_Switch:
  1573. ; We KNOW the args are upper case, because we made them so!
  1574.     mov    al,VERBO            ;assume it's 'v'
  1575.     cmp    ah,'V'                ;'V'?
  1576.     jz    Found_Switch            ;yep
  1577.     cmp    ah,'M'                ;'M'?
  1578.     jnz    Chk_VRelup            ;nope
  1579.     mov    al,MONOSYL            ;hah-it's monosyllabic
  1580.  
  1581. Found_Switch:
  1582.      cmp    byte ptr 2[si],0        ;Just the '-v'?
  1583.      jnz    Chk_VRelup            ;nope, must be a name
  1584.       mov    verbose,al            ;got switch,set verbose
  1585.       mov    byte ptr [si],0            ;clear this argv
  1586.       cmp    cx,argc                ;first argc?
  1587.       jnz    Chk_V_NoShift            ;nope
  1588.        inc    argc                ; .. sigh .. readjust
  1589.        call    _Shift                ;yep, move other args down one
  1590.                         ;(will decr argc again)
  1591. Chk_V_NoShift:
  1592.        dec    argc                ;final decr to eliminate it
  1593.        jmp    short Chk_ZipName        ;done
  1594.  
  1595. Chk_VRelup:
  1596.       add    bx,2                ;next argv^
  1597.       loop    Chk_VSwitch            ;check all args
  1598.  
  1599. ;If argv(1) was the '-v' switch,
  1600. ;or that other one,
  1601. ;that argv has been cleared via the _Shift call.
  1602. ;The target zip file name HAS to be argv(1)!
  1603.  
  1604. Chk_ZipName:
  1605.  
  1606.     mov    cx,argc                ;argc arg counter
  1607.     jcxz    Cmd_Err                ;he only had a switch!
  1608.  
  1609.     mov    bx,offset argv[2]        ;^argv(1)
  1610.     mov    si,[bx]                ;argv^(argc)
  1611.     call    Parse_MoveZipName        ;handle the zip name
  1612.     call    _Shift                ;move argv's down one
  1613.  
  1614. ;Check for member testing (2d or 3d argv).
  1615. ;We make the call even if argc = 0 (to clear a buffer)
  1616.  
  1617. Chk_MbrName:
  1618.     mov    di,offset pname1
  1619.     mov    byte ptr [di], 0        ;initialize mbr name
  1620.     mov    cx,argc                ;argc counter
  1621.     jcxz    Parse_Done            ;all done, no member
  1622.  
  1623.     mov    bx,offset argv[2]        ;it's now ^argv(2)
  1624.     mov    si,[bx]                ;argv^(argc)
  1625. Chk_MbrCopy:                    ;copy name to safe place
  1626.     lodsb
  1627.     stosb
  1628.     or    al, al                ;at end?
  1629.     jnz    Chk_MbrCopy            ;loop if not
  1630.  
  1631. Parse_Done:
  1632.     clc
  1633.     ret
  1634.  
  1635. Parse_CmdLine    endp
  1636.  
  1637. ;Cmdline parsing subroutine
  1638. ;Moves argv into our targetfile buffer,
  1639. ;picks up a pointer to past the paths (if any)
  1640. ;SI -> argv
  1641.  
  1642. Parse_MoveZipName    proc    near
  1643.  
  1644.     push    si                ;save SI
  1645.  
  1646.     mov    bx,offset ziptarget        ;full zip target filename
  1647.  
  1648.     mov    di,bx
  1649.     mov    cx,64                ;clear ziptarget
  1650.     xor    ax,ax
  1651.     rep    stosw
  1652.  
  1653.     mov    di,bx                ;DI -> ziptarget
  1654.  
  1655. Move_ZipName:
  1656.     lodsb                    ;snarf cmdline char
  1657.     or    al,al                ;AsciiZ terminator?
  1658.     jz    Skip_Zip_Path            ;yep
  1659.      stosb                    ;stuff
  1660.      jmp    Move_ZipName
  1661.  
  1662.  
  1663. Skip_Zip_Path:
  1664.  
  1665. ;DI -> last ziptarget's real char +1
  1666.  
  1667.     mov    dx,di                ;end, last char +1
  1668.     sub    dx,bx                ;end - start = length
  1669.  
  1670.     dec    di                ;adjust from last movsb
  1671.     std                    ;backwards
  1672.  
  1673.     mov    si,di                ;SI -> ziptarget last char
  1674.  
  1675. ;BX = ziptarget start
  1676. ;SI -> ziptarget lastchar
  1677. ;DX = ziptarget length
  1678. ;DI -> ziptarget lastchar
  1679.  
  1680. ;Now find any path separators in ziptarget
  1681.  
  1682.     mov    cx,dx                ;scan length
  1683.     mov    al,'\'                ;subdir dividers
  1684.     repne    scasb                ;scan till we hit one
  1685.     jz    Zip_GotPath            ;DI -> char before the '\'
  1686.  
  1687.     mov    di,si                ;back to last char
  1688.     mov    cx,dx                ;scan length
  1689.     mov    al,':'                ;how about just drive?
  1690.     repne    scasb
  1691.     jnz    Zip_NoPath            ;nope, DI -> ziptarget-1
  1692.                         ;skip the extra inc
  1693. Zip_GotPath:
  1694.     inc    di                ;bump to '\' or ':'
  1695. Zip_NoPath:
  1696.     inc    di                ;and past it to filename's char
  1697.     cld                    ;forward again
  1698.     mov    znameptr,di            ;points to start of
  1699.                         ;true filename (beyond path)
  1700.                         ;for later appending Find Next
  1701.                         ;filenames to path
  1702.  
  1703. ;append type of .ZIP, if not specified. Moved here from above in
  1704.     mov    dx,si                ;end of file name
  1705.     mov    cx,dx
  1706.     sub    cx,di                ;end - start = length
  1707.     inc    cx                ;well, nearly...
  1708.  
  1709.     mov    al,'.'
  1710.     repne    scasb                ;find a .TYP separator
  1711.  
  1712. ;DI -> char after '.' or actual filename end
  1713.     jz    PZN_Exit            ;leave .TYP alone!
  1714.      mov    si,offset ziptyp        ;assume need full '.ZIP'
  1715.      movsw                    ;copy .ZIP in
  1716.      movsw                    ;(4 chars)
  1717.  
  1718. PZN_Exit:
  1719.     pop    si                ;restore
  1720.     ret
  1721.  
  1722. Parse_MoveZipName    endp
  1723.  
  1724.  
  1725. Capitalize    Proc    Near
  1726.     ;    This routine takes a pointer to an ASCIIZ string and uppercases the
  1727.     ;    latter in place, taking national characters into account
  1728.  
  1729.     ;    Input
  1730.     ;        si - pointer to string
  1731.     ;    Registers modified
  1732.     ;        ax, cx, di, si
  1733. ;    National characters .. Gad!  Sure do appreciate
  1734. ;    the European outlook .. we provincial New World types ...
  1735.  
  1736.         Inc    si
  1737.         mov    ah,20H            ;handy constant
  1738. CNextChar:    Lodsb                ; get next char of string
  1739.         Or    al, al
  1740.         Jz    CExit            ; branch if at end of string
  1741.         Cmp    al, 'a'
  1742.         Jb    CNextChar        ; skip this char if below 'a'
  1743.         Cmp    al, 'z'            ; if above 'z', then...
  1744.         Ja    CUpperHalf        ; ...test for national chars
  1745.          Sub    al,ah    ; 20h        ; otherwise make upper case
  1746.          Jmp Short CPutChar        ; branch for replacement
  1747.  
  1748. CUpperHalf:    mov    cx, LowerSet-UpperSet    ; number of national chars
  1749.         mov    di, offset LowerSet    ; pointer to lower case letters
  1750.         repne scasb            ; try to find
  1751.         jne    CNextChar        ; loop if not found
  1752.         mov    al, [di+UpperSet-LowerSet-1] ; otherwise replace
  1753.  
  1754. CPutChar:    Mov    byte ptr [si-1], al    ; store back
  1755.         Jmp Short CNextChar        ; and loop
  1756.  
  1757. CExit:        Ret
  1758.  
  1759. Capitalize    EndP
  1760.  
  1761.  
  1762. ;---- args.asm ----------------------------------------------------------
  1763. ;from KEGELUNX.ARC Unix-like utils.
  1764.  
  1765. ; Args parses the command line into a unix-style parameter array.
  1766. ; CALL _ARGS to have it parse the cmdline.
  1767. ; Argc and Argv are just as in C; argc = # of params on cmd line,
  1768. ; argv is an array of pointers to strings (null terminated, of course).
  1769. ; Because MS-DOS doesn't pass us the name used to invoke the program,
  1770. ; argv[0] always points to a null string.
  1771. ; _Shift updates argc.
  1772.  
  1773. ;--------------------------------------------------------------------------
  1774.  
  1775. ; Maximum number of parameters
  1776. MAXPARMS    equ    4
  1777.  
  1778. _Args    proc    near
  1779.  
  1780. ; initialize
  1781.     cld                ; clear decrement mode
  1782.     mov    bx, 2            ; argc = 0 (will sub 2 from bx at end)
  1783.     mov    argv[0], offset null    ; prog name unknown; set to null.
  1784.     mov    si,offset params    ; i = 0
  1785.     mov    cl,nchar        ; cx = # of chars in command line
  1786.     xor    ch,ch
  1787.     jcxz    Args_Done        ; no arg chars -> we're done.
  1788.  
  1789.     mov    di, si            ; pointer to end of cmd line
  1790.     add    di, cx
  1791.     mov    [di+1],ch    ; 0    ; ASCIIZ-ify command line
  1792.     push    si            ; save vitals parms
  1793.     push    cx
  1794.     call    Capitalize        ; convert to uppercase in situ
  1795.     pop    cx            ; retrieve vital parms
  1796.     pop    si
  1797.  
  1798.     mov    di,offset argbuff    ;big arg buffer
  1799.  
  1800. ; Move arguments out of default DTA.
  1801.     push    cx
  1802.     push    di
  1803.     rep    movsb
  1804.     pop    si
  1805.     pop    cx
  1806.  
  1807. ; Big loop- find arguments...
  1808.     mov    ah,20H            ;handy space
  1809. ParmL:
  1810.  
  1811. ; Little loop #1: strip leading blanks from argument.
  1812. ; while (i<NCHAR && params[i] = ' ') do i++;
  1813. StripL:    lodsb                ; al = [si++]
  1814.     cmp    al,ah            ; space?
  1815.     loopz    StripL            ; if so, keep skipping.
  1816.     jne    Args_GotOne        ; If we found a nonblank,
  1817.                     ; skip zero test.
  1818.     jcxz    Args_Done        ; found no unblank chars -> we're done.
  1819.  
  1820. Args_GotOne:
  1821.     dec    si            ; bump SI back to start of nonblank.
  1822.     inc    cx
  1823.     mov    argv[bx],si        ; save pointer to this string
  1824.  
  1825. ; Little loop #2: skip nonblank chars.
  1826. ; while (i<NCHAR && params[i] <> ' ') do i++;
  1827.  
  1828. SkipL:    lodsb
  1829.     cmp    al,ah
  1830.     loopnz    SkipL
  1831.     jz    Oky
  1832.                     ; Last char of line was not blank.
  1833.      inc    si            ; make next statement put null
  1834.                     ; AFTER arg.
  1835. Oky:
  1836.     mov    byte ptr [si][-1], 0    ; put null at end of arg
  1837.     add    bx,2            ; argc++
  1838.  
  1839.     jcxz    Args_Done        ; if we ran off end of cmdline,
  1840.                     ; no more args.
  1841.  
  1842.     cmp    bx, MAXPARMS*2
  1843.     jb    ParmL            ; loop if argc < MAXPARMS.
  1844.  
  1845. Args_Done:
  1846. ; All done finding parms; now share argc with caller.
  1847.     mov    ax,bx
  1848.     sub    ax, 2
  1849.     shr    ax, 1
  1850.     mov    argc,ax            ;save argc
  1851.  
  1852.     ret
  1853.  
  1854. _Args    endp
  1855.  
  1856. ;---- _Shift: --------------------------------------------
  1857. ; Shifts %2 to %1, %3 to %2, etc.  Leaves %0 alone.
  1858. ; Works by shuffling argv[*].
  1859.  
  1860. _Shift    proc    near
  1861.     cld
  1862.     mov    si, offset argv[4]
  1863.     mov    di, offset argv[2]
  1864.     mov    cx, MAXPARMS
  1865.     rep    movsw
  1866.     dec    argc
  1867.     ret
  1868. _Shift    endp
  1869.  
  1870.  
  1871. ;---- parameter count, array ----------------
  1872. ; Pointers to up to MAXPARMS parameter strings are held here.
  1873.  
  1874. null    dw    0            ; the null string
  1875. argc    dw    ?
  1876. argv    dw    MAXPARMS dup (null)
  1877.  
  1878.  
  1879. ;Dynamic variables start here.
  1880. ;Not REAL big .. we could calculate them and release unneeded memory ..
  1881. ;but who needs it?  This ain't TSR, and the normal 64Kb required for
  1882. ;a .COM program shouldn't stress anyone's system capacity.
  1883.  
  1884. dta1BA        equ    $            ;alternate DTA
  1885. ziptarget    equ    dta1BA + 42        ;up to 128 bytes
  1886. pname1        equ    ziptarget + 128        ;11-byte FCB filename + 0
  1887. argbuff        equ    pname1 + 14        ;cmdline parsing buff
  1888.  
  1889. Comment        ~ Looks like:
  1890. +filename.typ 000K / 000K  +filename.typ 000K / 000K  +filename.typ 000K / 000K
  1891. Comment ends    ~
  1892. linebuff    equ    argbuff            ;82 chars long
  1893. dirbuff        equ    linebuff + 82        ;ENDOFS bytes long
  1894.  
  1895.  
  1896. CSEG    ENDS
  1897.     END    Zdir
  1898.